﻿using System;
using System.Linq;

using MiP.Common;

namespace MiP.Xml.Core.StringEvaluation
{
    internal class ReadVariableNameEvaluationstate : EvaluationState
    {
        internal override EvaluationState Next(EvaluationContext context)
        {
            // NOTE: To add default values, like in ${servername=moon} continue here with a new evaluation state which reads a default value.

            char currentChar = context.ReadNextChar();
            if (currentChar != '}')
            {
                context.Incomplete.Append(currentChar);
                context.VariableName.Append(currentChar);
                return this;
            }

            context.Incomplete.Clear();
            string variableName = context.VariableName.ToString();
            if (!context.Variables.ContainsKey(variableName))
            {
                // unknown variable will result in complete ${variablename} to be appended to result.
                variableName = "${" + variableName + "}";
                context.Result.Append(variableName);
                context.VariableName.Clear();
                context.Logger.Warn("WARNING: {0} is unknown.", variableName);
                return new ReadToDollarEvaluationState();
            }

            string variableValue = context.Variables[variableName];

            if (variableValue.Contains("${"))
                variableValue = RecursiveEvaluate(context, variableName, variableValue);

            context.Result.Append(variableValue);
            context.VariableName.Clear();

            return new ReadToDollarEvaluationState();
        }

        private string RecursiveEvaluate(EvaluationContext context, string variableName, string variableValue)
        {
            // check for circular dependencies
            if (context.CurrentOpenVariables.Contains(variableName))
            {
                string message = string.Format("Found circular dependency in variables: {0}.", string.Join("->", context.CurrentOpenVariables.Concat(new[] {variableName})));
                throw new InvalidOperationException(message);
            }

            // add the variable to variables being evaluated (recursive).
            context.CurrentOpenVariables.Add(variableName);

            EvaluationContext innerContext = new EvaluationContext(variableValue, context.Variables, context.Logger, context.CurrentOpenVariables);
            variableValue = innerContext.Evaluate();

            // variable was evaluated and can be removed
            context.CurrentOpenVariables.Remove(variableName);

            context.Variables[variableName] = variableValue;
            return variableValue;
        }
    }
}